﻿using System;
using System.Globalization;

namespace CyberConnect.Insteon.Messaging
{
    public struct MessageConfiguration
    {
        private readonly byte _Configuration;

        internal MessageConfiguration(MessageType messageType, bool isExtended, int hopsRemaining, int maxHops)
        {
            if (hopsRemaining > 3) { throw new ArgumentOutOfRangeException("hopsRemaining"); }
            if (maxHops > 3) { throw new ArgumentOutOfRangeException("maxHops"); }

            this._Configuration = (byte)((int)messageType | (isExtended ? 16 : 0) | ((int)hopsRemaining << 2) | ((int)maxHops));
        }

        public MessageConfiguration(MessageType messageType, bool isExtended)
        {
            this._Configuration = (byte)((int)messageType | (isExtended ? 16 : 0) | (3 << 2) | 3);
        }

        internal MessageConfiguration(int configuration)
            : this((MessageType)(configuration & MessageConfigurationMasks.Type)
                , (configuration & MessageConfigurationMasks.Extended) > 0
                , ((configuration & MessageConfigurationMasks.HopsLeft) >> 2)
                , (configuration & MessageConfigurationMasks.MaxHops)) { }

        public MessageType MessageType { get { return (MessageType)(this._Configuration & (int)MessageConfigurationMasks.Type); } }

        public int HopsRemaining { get { return (_Configuration & MessageConfigurationMasks.HopsLeft) >> 2; } }

        public int MaxHops { get { return _Configuration & MessageConfigurationMasks.MaxHops; } }

        public bool IsExtended { get { return (_Configuration & MessageConfigurationMasks.Extended) > 0; } }

        /// <summary>
        /// Contains masks useful for seperating configuration bits
        /// </summary>
        private static class MessageConfigurationMasks
        {
            public const byte Type = 128 + 64 + 32; //1110000
            public const byte Extended = 16; //00010000
            public const byte HopsLeft = 8 + 4; //00001100
            public const byte MaxHops = 2 + 1; //00000011

            public const byte AckNak = 128 + 32; //1010000

            public static bool IsAckNak(byte value)
            {
                int MessageType = value & MessageConfigurationMasks.AckNak;
                return MessageType == 32 || MessageType == 160;
            }

            public static bool IsAck(byte value)
            {
                return (value & MessageConfigurationMasks.AckNak) == 32;
            }

            public static bool IsNak(byte value)
            {
                return (value & MessageConfigurationMasks.AckNak) == 160;
            }
        }

        public bool IsResponse
        {
            get
            {
                return MessageConfigurationMasks.IsAckNak(this._Configuration);
            }
        }

        public bool IsAck
        {
            get
            {
                return MessageConfigurationMasks.IsAck(this._Configuration);
            }
        }

        public bool IsNak
        {
            get
            {
                return MessageConfigurationMasks.IsNak(this._Configuration);
            }
        }

        public override bool Equals(object obj)
        {
            return obj == null ? false
                : !(obj is MessageConfiguration) ? false
                    : Equals((MessageConfiguration)obj);
        }

        public override int GetHashCode()
        {
            return this._Configuration.GetHashCode();
        }

        public bool Equals(MessageConfiguration other)
        {
            return this._Configuration == other._Configuration;
        }

        public static bool operator ==(MessageConfiguration leftValue, MessageConfiguration rightValue)
        {
            return leftValue.Equals(rightValue);
        }

        public static bool operator !=(MessageConfiguration leftValue, MessageConfiguration rightValue)
        {
            return !(leftValue == rightValue);
        }

        public override string ToString()
        {
            return string.Format(CultureInfo.CurrentCulture, "MessageType:{0};IsExtended:{1};HopsRemaining:{2};MaxHops:{3};", this.MessageType, this.IsExtended, this.HopsRemaining, this.MaxHops);
        }

        public static MessageConfiguration FromInt32(int value)
        {
            return (MessageConfiguration)value;
        }

        public static explicit operator MessageConfiguration(int value)
        {
            return new MessageConfiguration(value);
        }

        public static int ToInt32(MessageConfiguration value)
        {
            return (int)value;
        }

        public int ToInt32()
        {
            return (int)this;
        }

        public byte ToByte()
        {
            return this._Configuration;
        }

        public static byte ToByte(MessageConfiguration value)
        {
            return value._Configuration;
        }

        public static explicit operator int(MessageConfiguration value)
        {
            return value._Configuration;
        }

        public static readonly MessageConfiguration Empty = (MessageConfiguration)0;
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
    public enum MessageType
    {
        Direct = 0,
        DirectAck = 32,
        DirectNak = 160,
        Broadcast = 128,
        GroupBroadcast = 192,
        GroupCleanupDirect = 64,
        GroupCleanupDirectAck = 96,
        GroupCleanupDirectNak = 224
    }
}